package genetic;

import genetic.individual.fitness.MarioIndividualFitnessPatterns;

import java.util.Random;

import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.sprites.Enemy;
import dk.itu.mario.engine.sprites.SpriteTemplate;
import dk.itu.mario.level.Level;

public class GeneticLevel extends Level {
	private int zones[];
	private int difficulty;
	private int type;
	private int gaps;

	protected static final byte[] chao       = {GROUND,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] gomba      = {GROUND,Enemy.ENEMY_GOOMBA,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] greenKOOPA = {GROUND,Enemy.ENEMY_GREEN_KOOPA,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] redKOOPA   = {GROUND,Enemy.ENEMY_RED_KOOPA,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] parede     = {GROUND,-1,-1,-1,BLOCK_EMPTY,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] parede2    = {GROUND,-1,-1,-1,BLOCK_EMPTY,-1,-1,-1,BLOCK_COIN,-1,-1,-1,-1,-1,-1};
	protected static final byte[] surpresa   = {GROUND,-1,-1,-1,BLOCK_POWERUP,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] surpresah  = {GROUND,-1,-1,-1,BLOCK_EMPTY,-1,-1,-1,BLOCK_POWERUP,-1,-1,-1,-1,-1,-1};
	protected static final byte[] surpresa2  = {GROUND,-1,-1,-1,BLOCK_COIN,-1,-1,-1,BLOCK_POWERUP,-1,-1,-1,-1,-1,-1};
	protected static final byte[] canoL      = {GROUND,TUBE_SIDE_LEFT,TUBE_TOP_LEFT,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] canoLF     = {GROUND,TUBE_SIDE_LEFT,TUBE_TOP_LEFT,Enemy.ENEMY_FLOWER,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] canoR      = {GROUND,TUBE_SIDE_RIGHT,TUBE_TOP_RIGHT,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] pedra1     = {GROUND,ROCK,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] pedra2     = {GROUND,ROCK,ROCK,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] pedra3     = {GROUND,ROCK,ROCK,ROCK,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] pedra4     = {GROUND,ROCK,ROCK,ROCK,ROCK,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] buraco     = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] chao1      = {GROUND,HILL_TOP,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
	protected static final byte[] chao2      = {GROUND,GROUND,HILL_TOP,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};

	public GeneticLevel(int width, int height){
		super(width, height);
	}

	public GeneticLevel(long seed, int difficulty, int type, int[] zones){
		this(zones.length + 64 + 10, chao.length);
		this.zones = zones;
		this.difficulty = difficulty;
		this.type = type;
		create();
	}

	public void create(){
		//create the start location
		int length = 0;
		length += buildStraight(0, 10);

		//create all of the medium sections
		int i = 0;
		while (length < width - 64 && i < zones.length){
			length += buildSlice(length, zones[i]);
			i++;
		}

		//set the end piece
		Random random = new Random();
		int floor = height - 3;

		xExit = length + 8;
		yExit = floor;

		// fills the end piece
		for (int x = length; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				if (y >= floor)
				{
					setBlock(x, y, GROUND);
				}
			}
		}

		if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
		{
			int ceiling = 0;
			int run = 0;
			for (int x = 0; x < width; x++)
			{
				if (run-- <= 0 && x > 4)
				{
					ceiling = random.nextInt(4);
					run = random.nextInt(4) + 4;
				}
				for (int y = 0; y < height; y++)
				{
					if ((x > 4 && y <= ceiling) || x < 1)
					{
						setBlock(x, y, GROUND);
					}
				}
			}
		}

		fixWalls();

	}
	private int buildSlice(int x, int zoneType){
		switch (zoneType) {
		case MarioIndividualFitnessPatterns.chao:
			build(x,chao);
			return 1;
		case MarioIndividualFitnessPatterns.gomba:
			build(x,gomba);
			return 1;
		case MarioIndividualFitnessPatterns.greenKOOPA:
			build(x,greenKOOPA);
			return 1;
		case MarioIndividualFitnessPatterns.redKOOPA:
			build(x,redKOOPA);
			return 1;
		case MarioIndividualFitnessPatterns.parede:
			build(x,parede);
			return 1;
		case MarioIndividualFitnessPatterns.parede2:
			build(x,parede2);
			return 1;
		case MarioIndividualFitnessPatterns.surpresa:
			build(x,surpresa);
			return 1;
		case MarioIndividualFitnessPatterns.surpresah:
			build(x,surpresah);
			return 1;
		case MarioIndividualFitnessPatterns.surpresa2:
			build(x,surpresa2);
			return 1;
		case MarioIndividualFitnessPatterns.cano:
			build(x,canoL);
			build(x+1, canoR);
			return 2;
		case MarioIndividualFitnessPatterns.canoF:
			build(x,canoLF);
			build(x+1, canoR);
			return 2;
		case MarioIndividualFitnessPatterns.pedra1:
			build(x,pedra1);
			return 1;
		case MarioIndividualFitnessPatterns.pedra2:
			build(x,pedra2);
			return 1;
		case MarioIndividualFitnessPatterns.pedra3:
			build(x,pedra3);
			return 1;
		case MarioIndividualFitnessPatterns.pedra4:
			build(x,pedra4);
			return 1;
		case MarioIndividualFitnessPatterns.buraco:
			build(x,buraco);
			return 1;
		case MarioIndividualFitnessPatterns.chao1:
			build(x,chao1);
			return 1;
		case MarioIndividualFitnessPatterns.chao2:
			build(x,chao2);
			return 1;
		}
		return 0;
	}

	private void build(int x, byte[] slice){
		for(int i = 0; i < slice.length; i++){
			if(slice[i] != -1){
				if(slice[i] >= Enemy.ENEMY_RED_KOOPA && slice[i] <= Enemy.ENEMY_SPIKY)
					setSpriteTemplate(x, slice.length-1-i, new SpriteTemplate(slice[i], false));
				else if(slice[i] == Enemy.ENEMY_FLOWER)
					setSpriteTemplate(x, slice.length-i, new SpriteTemplate(slice[i], false));
				else
					setBlock(x, slice.length-1-i, slice[i]);
			}
		}
	}
	
	private int buildStraight(int xo, int length){

		int floor = height - 3;

		//runs from the specified x position to the length of the segment
		for (int x = xo; x < xo + length; x++){
			for (int y = 0; y < height; y++){
				if (y >= floor){
					setBlock(x, y, GROUND);
				}
			}
		}

		return length;
	}

	private void fixWalls(){
		boolean[][] blockMap = new boolean[width + 1][height + 1];
		for (int x = 0; x < width + 1; x++){
			for (int y = 0; y < height + 1; y++){
				int blocks = 0;
				for (int xx = x - 1; xx < x + 1; xx++){
					for (int yy = y - 1; yy < y + 1; yy++){
						if (getBlockCapped(xx, yy) == GROUND){
							blocks++;
						}
					}
				}
				blockMap[x][y] = blocks == 4;
			}
		}
		blockify(this, blockMap, width + 1, height + 1);
	}

	private void blockify(Level level, boolean[][] blocks, int width, int height){
		int to = 0;
		if (type == LevelInterface.TYPE_CASTLE){
			to = 4 * 2;
		}
		else if (type == LevelInterface.TYPE_UNDERGROUND){
			to = 4 * 3;
		}

		boolean[][] b = new boolean[2][2];

		for (int x = 0; x < width; x++){
			for (int y = 0; y < height; y++){
				for (int xx = x; xx <= x + 1; xx++){
					for (int yy = y; yy <= y + 1; yy++){
						int _xx = xx;
						int _yy = yy;
						if (_xx < 0) _xx = 0;
						if (_yy < 0) _yy = 0;
						if (_xx > width - 1) _xx = width - 1;
						if (_yy > height - 1) _yy = height - 1;
						b[xx - x][yy - y] = blocks[_xx][_yy];
					}
				}

				if (b[0][0] == b[1][0] && b[0][1] == b[1][1]){
					if (b[0][0] == b[0][1]){
						if (b[0][0]){
							level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
						}else{
							// KEEP OLD BLOCK!
						}
					}
					else{
						if (b[0][0]){
							//down grass top?
							level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
						}
						else{
							//up grass top
							level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
						}
					}
				}
				else if (b[0][0] == b[0][1] && b[1][0] == b[1][1]){
					if (b[0][0]){
						//right grass top
						level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
					}
					else{
						//left grass top
						level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
					}
				}
				else if (b[0][0] == b[1][1] && b[0][1] == b[1][0]){
					level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
				}
				else if (b[0][0] == b[1][0]){
					if (b[0][0]){
						if (b[0][1]){
							level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
						}
						else{
							level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
						}
					}
					else{
						if (b[0][1]){
							//right up grass top
							level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
						}
						else{
							//left up grass top
							level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
						}
					}
				}
				else if (b[0][1] == b[1][1]){
					if (b[0][1]){
						if (b[0][0]){
							//left pocket grass
							level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
						}
						else{
							//right pocket grass
							level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
						}
					}
					else{
						if (b[0][0]){
							level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
						}
						else{
							level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
						}
					}
				}
				else{
					level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
				}
			}
		}
	}

}
